
#include "stdafx.h"
#include "PngEncoder.h"

#pragma unmanaged

#include <fstream>


namespace DDesktopServer{

	PngEncoder::PngEncoder()
	{
		m_png_ptr = NULL;
		m_png_info_ptr = NULL;
	}

	PngEncoder::~PngEncoder()
	{
		if(m_png_ptr && m_png_info_ptr){
			png_destroy_write_struct(&m_png_ptr, &m_png_info_ptr);
		}
	}

	bool PngEncoder::Init(int width, int height)
	{
		// png_struct
		m_png_ptr=png_create_write_struct(
			PNG_LIBPNG_VER_STRING,
			NULL,
			NULL,
			NULL);
		if(m_png_ptr==NULL){
			return false;
		}

		// png_info
		m_png_info_ptr=png_create_info_struct(m_png_ptr);
		if(m_png_info_ptr==NULL){
			png_destroy_read_struct(&m_png_ptr,NULL,NULL);
			return false;
		}

		png_set_write_fn(m_png_ptr, (void*)this, PNGWrite, PNGFlush);
		png_set_bgr(m_png_ptr); // set byte order Blue Green Red

		png_set_IHDR(
			m_png_ptr,
			m_png_info_ptr,
			width,
			height,
			8,							//bit_depth
			PNG_COLOR_TYPE_RGB,			//color_type,
            PNG_INTERLACE_NONE,			//interlace_type,
			PNG_COMPRESSION_TYPE_BASE,	//compression_type,
			PNG_FILTER_TYPE_BASE		//filter_type
		);

		{ // init input img buf
			m_img_width = width;
			m_img_height = height;
			m_input_img_buf.resize(height);
		}

		m_write_buf.resize(width * height * 4);  //reserve max size

		{ // write header
			int i;
			m_write_pt = 0;
			png_write_info(m_png_ptr, m_png_info_ptr);
			m_header_buf.resize(m_write_pt);
			for(i = 0; i < m_write_pt; i++){
				m_header_buf[i] = m_write_buf[i];
			}
			m_write_pt = 0;
		}

		return true;
	}

	const unsigned char* PngEncoder::GetHeader(void) const
	{
		return (unsigned char*)&(m_header_buf[0]);
	}


	const unsigned char* PngEncoder::GetData(void) const
	{
		return (unsigned char*)&(m_write_buf[0]);
	}


	int PngEncoder::GetHeaderSize(void) const
	{
		return (int)m_header_buf.size();
	}

	int PngEncoder::GetDataSize(void) const
	{
		return m_write_pt;
	}

	bool PngEncoder::Encode(char* data, int size)
	{
		png_struct tmp = *m_png_ptr;
		m_write_pt = 0;

		for(int i = 0; i < m_img_height; i++){
			m_input_img_buf[i] = data + (i * m_img_width * 3);  // 3 bytes per pixel
		}

		png_write_image(m_png_ptr, (png_bytepp)&(m_input_img_buf[0]));
		png_write_end(m_png_ptr, m_png_info_ptr);

		*m_png_ptr = tmp;

		return true;
	}


	// for debug
	bool PngEncoder::WriteToFile(const char* file_name) const
	{
		std::ofstream fout(file_name, std::ios_base::binary);


		fout.write(&(m_header_buf[0]), m_header_buf.size());
		fout.write(&(m_write_buf[0]), m_write_pt);

		fout.close();

		return true;
	}


	void PngEncoder::PNGWrite(png_structp png_ptr, png_bytep data, png_size_t length)
	{

		PngEncoder* self = (PngEncoder*)png_get_io_ptr(png_ptr);
		char* pt = &(self->m_write_buf[self->m_write_pt]);
		self->m_write_pt += length;

		if((unsigned int)self->m_write_pt >= self->m_write_buf.size()){
			return;
		}

		while(length--){
			*pt++ = *data++;
		}
	}

	void PngEncoder::PNGFlush(png_structp png_ptr)
	{
	}
}
